﻿using System;
using Caliburn.Micro;
using SS.WPF.Framework.BackgroundAction;
using SS.WPF.Framework.Display.Dialog;
using Action = System.Action;

namespace SS.WPF.Framework.Coroutines
{
    /// <summary>
    /// The class is used to execute a action as coroutine
    /// </summary>
    public abstract class ResultBase: IFluentResult
    {
        #region Fields

        /// <summary>
        /// The main action, it will be run on UI thread
        /// </summary>
        protected Action _doAction;

        /// <summary>
        /// This action will be invoked to execute before main action executing, it will be run on UI thread
        /// </summary>
        protected Action _beforeAction;

        /// <summary>
        /// This action will be invoked to execute after main action executing, it will be run on UI thread
        /// </summary>
        protected Action _afterAction;

        /// <summary>
        /// This callback of main action
        /// </summary>
        protected Action _callbackAction;

        /// <summary>
        /// This action will be invoked to execute when exception raised in main action
        /// </summary>
        protected Action<Exception> _exceptionAction;

        /// <summary>
        /// THe handler for background action
        /// </summary>
        protected Action _unRegistryBackgroundAction;

        /// <summary>
        /// The object contains information of Action run on Background thread
        /// </summary>
        protected IBackgroundActionIndicator _backgroundActionIndicator = new BackgroundActionIndicator();

        /// <summary>
        /// Determines if the main action continuous to execute in spite of exception raising
        /// </summary>
        protected bool _continuousOnException;

        #endregion

        #region Implementations of IFluentResult

        public IFluentResult Do(Action action)
        {
            this._doAction = action;
            return this.GetActualResultInstance();
        }

        public IFluentResult Before(Action action)
        {
            this._beforeAction = action;
            return this.GetActualResultInstance();
        }

        public IFluentResult After(Action action)
        {
            this._afterAction = action;
            return this.GetActualResultInstance();
        }

        public IFluentResult OnException(Action<Exception> action)
        {
            this._exceptionAction = action;
            return this.GetActualResultInstance();
        }

        public IFluentResult OnCallback(Action action)
        {
            this._callbackAction = action;
            return this.GetActualResultInstance();
        }

        public IFluentResult ContinuousOnException(bool value)
        {
            this._continuousOnException = value;
            return this.GetActualResultInstance();
        }

        public IFluentResult Title(string title)
        {
            this._backgroundActionIndicator.Title = title;
            return this.GetActualResultInstance();
        }

        public IFluentResult BlockingUI()
        {
            this._backgroundActionIndicator.IsBlockingUI = true;
            return this.GetActualResultInstance();
        }

        public IFluentResult DialogManager(IDialogManager dialogManager)
        {
            this._backgroundActionIndicator.DialogManager = dialogManager;
            return this.GetActualResultInstance();
        }

        #endregion

        #region Implementations of IResult

        /// <summary>
        /// Execute action as Coroutines
        /// </summary>
        /// <param name="context"></param>
        public void Execute(ActionExecutionContext context)
        {
            try
            {
                this.RegisterTask(this._backgroundActionIndicator);
                this._unRegistryBackgroundAction = () => this.UnRegisterTask(this._backgroundActionIndicator);
                this.DoExecute();
            }
            catch (Exception ex)
            {
                this.UnRegisterTask(this._backgroundActionIndicator);
                
                this.HandleException(ex);

                if (this._continuousOnException)
                {
                    this.OnCompleted(this, null);
                }    
            }
        }

        /// <summary>
        /// The main logic of Execute
        /// </summary>
        protected virtual void DoExecute()
        {
            if (this._beforeAction != null)
            {
                this._beforeAction();
            }

            if (this._doAction != null)
            {
                this._doAction();
            }

            if (this._afterAction != null)
            {
                this._afterAction();
            }

            if (this._unRegistryBackgroundAction != null)
            {
                this._unRegistryBackgroundAction();
            }

            this.OnCompleted(this, null);
        }

        public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };

        #endregion

        #region Help Methods

        /// <summary>
        /// Process Exception
        /// </summary>
        /// <param name="ex">Specified exception</param>
        protected void HandleException(Exception ex)
        {
            if (this._exceptionAction != null)
            {
                this._exceptionAction(ex);
            }
        }

        /// <summary>
        /// Register an background action indicator to manager
        /// </summary>
        /// <param name="backgroundActionIndicator"></param>
        private void RegisterTask(IBackgroundActionIndicator backgroundActionIndicator)
        {
            IoC.Get<IBackgroundActionIndicatorsManager>().RegisterTask(backgroundActionIndicator);
        }

        /// <summary>
        /// Unregister background action indicator to manager
        /// </summary>
        /// <param name="backgroundActionIndicator"></param>
        private void UnRegisterTask(IBackgroundActionIndicator backgroundActionIndicator)
        {
            IoC.Get<IBackgroundActionIndicatorsManager>().UnRegisterTask(backgroundActionIndicator);
        }

        /// <summary>
        /// This method is used to mark that the IResult has been executed completing
        /// </summary>
        /// <param name="rslt"></param>
        /// <param name="ex"></param>
        protected void OnCompleted(IResult rslt, Exception ex)
        {
            if (this.Completed != null)
            {
                this.Completed(rslt, new ResultCompletionEventArgs
                                         {
                                             Error = ex
                                         });
            }
        }

        /// <summary>
        /// Get actual instance type of Result
        /// </summary>
        /// <returns>
        /// The actual instance of result
        /// </returns>
        protected abstract IFluentResult GetActualResultInstance();

        #endregion
    }
}
